// g++ testlinux.cpp -lpthread -lrt -lboost_system -lprofiler
#include <iostream>

#include <sys/socket.h>
#include <netinet/in.h>
#include <atomic>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>

// libgoogle-perftools-dev google-perftools
// google-pprof a.out gperf.prof --text > test.txt
// google-pprof a.out test.prof --web > test.html
// google-pprof a.out test.prof --pdf > test.pdf
#include <gperftools/profiler.h>

std::atomic_bool lis;

void *socket_basic_client(void *args)
{
    pthread_detach(pthread_self());

    auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    sockaddr_in addr_in = {0};
    addr_in.sin_family = AF_INET;
    addr_in.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr_in.sin_port = htons(12503);
    if (connect(sock, (const sockaddr *)&addr_in, sizeof(sockaddr_in)) != 0)
    {
        perror("connect failed");
        return nullptr;
    }

    char buf[100];

    while (lis)
    {
        auto t = time(nullptr);
        auto ts = ctime(&t);
        send(sock, ts, strlen(ts), 0);

        auto len = recv(sock, buf, 100, 0);
        if (len == -1)
        {
            perror("recv2 error");
            usleep(100000);
        }

        std::cout << "server time: ";
        std::cout.write(buf, len);
        std::cout << std::endl;

        sleep(1);
    }

    shutdown(sock, SHUT_RDWR);
    close(sock);
    return nullptr;
}

void *socket_basic_handle(void *args)
{
    pthread_detach(pthread_self());

    auto sock = *(int *)args;

    char buf[1024];

    while (lis)
    {
        auto len = recv(sock, buf, 1024, 0);
        if (len == -1)
        {
            perror("recv error");
            usleep(100000);
        }

        std::cout << "client time: ";
        std::cout.write(buf, len);
        std::cout << std::endl;

        auto t = time(nullptr);
        auto ts = ctime(&t);
        send(sock, ts, strlen(ts), 0);
    }

    shutdown(sock, SHUT_RDWR);
    close(sock);
    return nullptr;
}

#include <pthread.h>

void socket_basic()
{
    auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    auto reuse = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) != 0)
    {
        perror("set socket options error");
    }

    sockaddr_in addr_in = {0};
    addr_in.sin_family = AF_INET;
    addr_in.sin_port = htons(12503);
    if (bind(sock, (const sockaddr *)&addr_in, sizeof(sockaddr_in)) != 0)
    {
        perror("bind failed");
        return;
    }

    if (listen(sock, 5) != 0)
    {
        perror("listen failed");
        return;
    }

    pthread_t thd;
    if (pthread_create(&thd, nullptr, socket_basic_client, nullptr) != 0)
    {
        perror("create thread failed");
        return;
    }

    // 定时退出
    if (pthread_create(
            &thd, nullptr, [](void *args) -> void *
            {
                pthread_detach(pthread_self());

                auto sock = *(int *)args;
                auto begin = time(nullptr);
                while (lis)
                    if (difftime(time(nullptr), begin) > 20)
                        break;
                lis = false;
                shutdown(sock, SHUT_RDWR);
                close(sock);
                return nullptr; },
            &sock) != 0)
    {
        perror("create thread failed");
        return;
    }

    sockaddr addr;
    socklen_t len;
    int nsock;
    lis = true;
    while (lis)
    {
        nsock = accept(sock, &addr, &len);
        if (nsock == -1)
        {
            perror("accept error");
            usleep(100000);
            continue;
        }

        if (pthread_create(&thd, nullptr, socket_basic_handle, (void *)&nsock) != 0)
        {
            perror("create thread error");

            shutdown(nsock, SHUT_RDWR);
            close(nsock);
            continue;
        }

        usleep(100000);
    }
}

#include <fcntl.h>
#include <netdb.h>

void *socket_nonblock_handle(void *args)
{
    pthread_detach(pthread_self());

    auto sock = *(int *)args;

    fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

    sockaddr_in addr, laddr;
    socklen_t addr_len;
    getsockname(sock, (sockaddr *)&laddr, &addr_len);
    getpeername(sock, (sockaddr *)&addr, &addr_len);

    auto host = gethostbyaddr(&addr.sin_addr, sizeof(in_addr), AF_INET);
    if (host == nullptr)
    {
        herror("gethostbyaddr");
        std::cout << inet_ntoa(addr.sin_addr) << ":" << addr.sin_port << " connected" << std::endl;
    }
    else
        std::cout << inet_ntoa(addr.sin_addr) << "(" << host->h_name << "):" << addr.sin_port << " connected" << std::endl;

    char buf[100];

    while (true)
    {
        auto t = time(nullptr);
        auto ts = ctime(&t);
        auto len = send(sock, ts, strlen(ts), 0);
        if (len == -1)
        {
            if (errno != EAGAIN)
            {
                perror("send error");
                break;
            }

            usleep(200000);
            continue;
        }

        if (len == 0)
            break;

        len = recv(sock, buf, 100, 0);
        if (len == -1)
        {
            if (errno != EAGAIN)
            {
                perror("recv error");
                break;
            }

            usleep(200000);
            continue;
        }

        if (len == 0)
            break;

        std::cout.write(buf, len);
        std::cout << std::endl;
    }

    std::cout << inet_ntoa(addr.sin_addr) << ":" << addr.sin_port << " disconnected" << std::endl;

    shutdown(sock, SHUT_RDWR);
    close(sock);
    return nullptr;
}

void socket_nonblock()
{
    auto sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);

    auto reuse = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));

    sockaddr_in addr_in = {0};
    addr_in.sin_family = AF_INET;
    addr_in.sin_port = htons(15033);
    addr_in.sin_addr.s_addr = inet_addr("127.0.0.1");
    bind(sock, (const sockaddr *)&addr_in, sizeof(sockaddr_in));

    listen(sock, 5);

    pthread_t thd;
    sockaddr addr;
    socklen_t len;
    while (true)
    {
        auto nsock = accept(sock, &addr, &len);
        if (nsock == -1)
        {
            if (errno != EAGAIN)
            {
                perror("accept error");
                break;
            }

            usleep(200000);
            continue;
        }

        pthread_create(&thd, nullptr, socket_nonblock_handle, &nsock);

        usleep(200000);
    }

    shutdown(sock, SHUT_RDWR);
    close(sock);
}

#include <map>

void socket_select()
{
    auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    auto reuse = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
    fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(13045);
    bind(sock, (const sockaddr *)&addr, sizeof(sockaddr_in));

    listen(sock, 5);

    fd_set fdsr;
    std::map<int, std::string> fds;
    int maxfdp;
    sockaddr paddr;
    socklen_t addr_len;
    sockaddr_in *ppaddr = (sockaddr_in *)&paddr;
    char peername[25];
    getsockname(sock, &paddr, &addr_len);

    char buf[256];

    while (true)
    {
        FD_ZERO(&fdsr);
        FD_SET(sock, &fdsr);
        maxfdp = sock;
        for (auto it = fds.begin(); it != fds.end(); it++)
        {
            auto fd = it->first;

            if (fd > maxfdp)
                maxfdp = fd;

            FD_SET(fd, &fdsr);
        }
        ++maxfdp;

        auto ret = select(maxfdp, &fdsr, nullptr, nullptr, nullptr);
        if (ret == 0)
        {
            usleep(100000);
            continue;
        }

        if (ret == -1)
        {
            perror("select");
            break;
        }

        // 监听
        if (FD_ISSET(sock, &fdsr))
        {
            auto nsock = accept(sock, &paddr, &addr_len);
            if (nsock == -1)
            {
                if (errno == EAGAIN)
                {
                    perror("accept");
                }
            }
            else
            {
                fcntl(nsock, F_SETFL, fcntl(nsock, F_GETFL, 0) | O_NONBLOCK);

                std::sprintf(peername, "%s:%d\0", inet_ntoa(ppaddr->sin_addr), ppaddr->sin_port);
                fds[nsock] = peername;

                std::cout << peername << " connected" << std::endl;
            }
        }

        // 读
        for (auto it = fds.begin(); it != fds.end();)
        {
            auto fd = it->first;
            it++;

            if (!FD_ISSET(fd, &fdsr))
                continue;

            auto len = recv(fd, &buf, 256, 0);
            if (len > 0)
            {
                std::cout.write(buf, len);
                std::cout << std::endl;
            }
            else if (len == -1)
                perror("recv");
            else if (len == 0)
            {
                std::cout << fds[fd] << " disconnected" << std::endl;

                fds.erase(fd);
                shutdown(fd, SHUT_RDWR);
                close(fd);
            }
        }
    }

    shutdown(sock, SHUT_RDWR);
    close(sock);
}

#include <poll.h>
#include <vector>

void socket_poll()
{
    auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    auto reuse = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
    fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

    sockaddr_in addr = {0};
    addr.sin_port = htons(14303);
    addr.sin_family = AF_INET;
    auto paddr = (sockaddr *)&addr;
    bind(sock, paddr, sizeof(sockaddr_in));

    listen(sock, 5);

    std::vector<pollfd> fds;
    pollfd lpfd = {sock, POLLIN, 0};
    fds.push_back(lpfd);
    std::map<int, std::string> fdmap;
    socklen_t addr_len;
    char peername[25];
    getpeername(sock, paddr, &addr_len);

    char buf[256];

    while (true)
    {
        auto ret = poll(fds.data(), fds.size(), -1);
        if (ret == -1)
        {
            perror("poll");

            usleep(100000);
            continue;
        }

        // 监听
        if (fds.front().revents != 0)
        {
            auto nsock = accept(sock, paddr, &addr_len);
            if (nsock == -1)
            {
                perror("accept");
            }
            else
            {
                fcntl(nsock, F_SETFL, fcntl(nsock, F_GETFL, 0) | O_NONBLOCK);

                pollfd pfd = {nsock, POLLIN, 0};
                fds.push_back(pfd);

                sprintf(peername, "%s:%d\0", inet_ntoa(addr.sin_addr), addr.sin_port);
                fdmap[nsock] = peername;

                std::cout << peername << " connected" << std::endl;
            }
        }

        // 读
        for (auto it = fds.begin(); it != fds.end();)
        {
            auto fd = *it;

            if (fd.revents == 0 || fd.fd == sock)
            {
                it++;
                continue;
            }

            auto len = recv(fd.fd, buf, 256, 0);
            if (len > 0)
            {
                std::cout.write(buf, len);
                std::cout << std::endl;

                it++;
                continue;
            }

            if (len == -1)
            {
                perror("recv");

                it++;
                continue;
            }

            if (len == 0)
            {
                std::cout << fdmap[fd.fd] << " disconnected" << std::endl;

                fdmap.erase(fd.fd);

                shutdown(fd.fd, SHUT_RDWR);
                close(fd.fd);

                it = fds.erase(it);
            }
        }
    }

    shutdown(sock, SHUT_RDWR);
    close(sock);
}

#include <sys/epoll.h>

#define BUFFER_SIZE 50

void socket_epoll()
{
    auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    auto reuse = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
    fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(15535);
    auto paddr = (sockaddr *)&addr;
    if (bind(sock, paddr, sizeof(sockaddr_in)) == -1)
    {
        perror("bind");
        return;
    }

    listen(sock, 10);

    auto ep = epoll_create(50);
    if (ep == -1)
    {
        perror("epoll_create");
        return;
    }

    epoll_event event = {0};
    event.events = EPOLLIN | EPOLLET;
    event.data.fd = sock;
    if (epoll_ctl(ep, EPOLL_CTL_ADD, sock, &event) == -1)
    {
        perror("epoll_ctl");
        return;
    }

    epoll_event events[256];
    socklen_t addr_len;
    char recv_buf[BUFFER_SIZE];
    std::map<int, std::string> fdmap;
    char peer_name[25];
    getsockname(sock, paddr, &addr_len);

    while (true)
    {
        auto num = epoll_wait(ep, events, 256, -1);
        if (num == -1)
        {
            perror("epoll_wait");

            usleep(100000);
            continue;
        }

        for (int i = 0; i < num; i++)
        {
            auto fd = events[i].data.fd;

            if (fd == sock)
            {
                auto nsock = accept(sock, paddr, &addr_len);
                if (nsock == -1)
                {
                    perror("accept");
                    exit(-1);
                }

                fcntl(nsock, F_SETFL, fcntl(nsock, F_GETFL, 0) | O_NONBLOCK);

                std::sprintf(peer_name, "%s:%d\0", inet_ntoa(addr.sin_addr), addr.sin_port);
                fdmap[nsock] = peer_name;
                std::cout << peer_name << " connected" << std::endl;

                event.data.fd = nsock;
                event.events = EPOLLIN | EPOLLET;
                if (epoll_ctl(ep, EPOLL_CTL_ADD, nsock, &event) == -1)
                {
                    perror("epoll_ctl");

                    shutdown(nsock, SHUT_RDWR);
                    close(nsock);
                    fdmap.erase(nsock);
                }

                continue;
            }

            if (events[i].events & EPOLLIN != 0)
            {
                auto len = recv(fd, &recv_buf, BUFFER_SIZE, 0);
                if (len > 0)
                {
                    std::cout.write(recv_buf, len);
                    std::cout << std::endl;

                    auto t = time(nullptr);
                    auto cs = ctime(&t);
                    if (send(fd, cs, strlen(cs), 0) == -1)
                    {
                        perror("send");

                        if (errno == EAGAIN)
                        {
                        }
                    }
                }
                else if (len == 0)
                {
                    std::cout << fdmap[fd] << " disconnected" << std::endl;
                    fdmap.erase(fd);

                    shutdown(fd, SHUT_RDWR);
                    close(fd);
                }
                else
                {
                    perror("recv");

                    if (errno == ECONNRESET)
                    {
                        std::cout << fdmap[fd] << " disconnected" << std::endl;
                        fdmap.erase(fd);

                        close(fd);
                    }
                }
            }
        }
    }

    shutdown(sock, SHUT_RDWR);
    close(sock);
    close(ep);
}

#include <signal.h>

std::map<int, std::string> fdmap;

void socket_signal()
{
    ProfilerStart("./gperf.prof");

    auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    sockaddr_in addr = {0};
    addr.sin_port = htons(7834);
    addr.sin_family = AF_INET;
    auto paddr = (sockaddr *)&addr;
    bind(sock, paddr, sizeof(sockaddr_in));

    listen(sock, 5);

    struct sigaction action = {0};
    action.sa_handler = [](int sig)
    {
        if (sig != SIGIO)
            return;

        char buf[64];

        for (auto it = fdmap.begin(); it != fdmap.end();)
        {
            auto fd = it->first;
            it++;

            auto len = recv(fd, buf, 64, 0);
            if (len > 0)
            {
                std::cout.write(buf, len);
                std::cout << std::endl;
            }
            else if (len == 0)
            {
                std::cout << fdmap[fd] << " disconnected" << std::endl;
                fdmap.erase(fd);

                shutdown(fd, SHUT_RDWR);
                close(fd);

                ProfilerStop();
            }
            else
            {
                if (errno == EAGAIN)
                    continue;

                perror("recv");

                if (errno == ECONNRESET)
                {
                    std::cout << fdmap[fd] << " disconnected" << std::endl;
                    fdmap.erase(fd);

                    close(fd);
                }
            }
        }
    };
    sigaction(SIGIO, &action, NULL);

    socklen_t addr_len;
    auto pid = getpid();
    char peername[25];
    getsockname(sock, paddr, &addr_len);

    while (true)
    {
        auto nsock = accept(sock, paddr, &addr_len);
        if (nsock == -1)
        {
            if (errno == EINTR)
                continue;

            perror("accept");
            break;
        }

        fcntl(nsock, F_SETOWN, pid);
        fcntl(nsock, F_SETFL, fcntl(nsock, F_GETFL, 0) | O_ASYNC | O_NONBLOCK);

        sprintf(peername, "%s:%d", inet_ntoa(addr.sin_addr), addr.sin_port);
        fdmap[nsock] = peername;
        std::cout << peername << " connected" << std::endl;
    }

    close(sock);
}

#include <aio.h>

// 信号
void socket_aio_signal()
{
    auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(7834);
    auto paddr = (sockaddr *)&addr;
    bind(sock, paddr, sizeof(sockaddr_in));

    listen(sock, 5);

    // 注册信号
    struct sigaction sig_act = {0};
    sig_act.sa_flags = SA_SIGINFO;
    sig_act.sa_sigaction = [](int signo, siginfo_t *info, void *context)
    {
        if (signo != SIGIO)
            return;

        auto req = (aiocb *)info->si_value.sival_ptr;

        if (aio_error(req) != 0)
            return;

        auto ret = aio_return(req);
        if (ret == 0)
        {
            std::cout << fdmap[req->aio_fildes] << " disconnected" << std::endl;
            fdmap.erase(req->aio_fildes);

            shutdown(req->aio_fildes, SHUT_RDWR);
            close(req->aio_fildes);
            return;
        }

        if (ret > 0)
        {
            std::cout.write((const char *)req->aio_buf, ret);
            std::cout << std::endl;
        }

        // 继续投递请求
        if (aio_read(req) == -1)
        {
            perror("aio_read");
        }
    };
    sigaction(SIGIO, &sig_act, NULL);

    socklen_t addr_len;
    char peername[25];
    getsockname(sock, paddr, &addr_len);

    while (true)
    {
        auto nsock = accept(sock, paddr, &addr_len);
        if (nsock == -1)
        {
            if (errno == EINTR)
                continue;

            perror("accept");
            break;
        }

        sprintf(peername, "%s:%d", inet_ntoa(addr.sin_addr), addr.sin_port);
        fdmap[nsock] = peername;
        std::cout << peername << " connected" << std::endl;

        aiocb acb = {0};
        acb.aio_fildes = nsock;
        acb.aio_buf = malloc(BUFFER_SIZE + 1);
        acb.aio_nbytes = BUFFER_SIZE;
        acb.aio_offset = 0;
        acb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
        acb.aio_sigevent.sigev_signo = SIGIO;
        acb.aio_sigevent.sigev_value.sival_ptr = &acb;

        if (aio_read(&acb) == -1)
        {
            perror("aio_read");
        }
    }

    close(sock);
}

// 线程
void socket_aio_completion_handler(sigval_t sigval)
{
    auto req = (aiocb *)sigval.sival_ptr;
    if (aio_error(req) != 0)
        return;

    auto ret = aio_return(req);
    if (ret == 0)
    {
        std::cout << fdmap[req->aio_fildes] << " disconnected" << std::endl;
        fdmap.erase(req->aio_fildes);

        shutdown(req->aio_fildes, SHUT_RDWR);
        close(req->aio_fildes);
        return;
    }

    if (ret > 0)
    {
        std::cout.write((const char *)req->aio_buf, ret);
        std::cout << std::endl;
    }

    if (aio_read(req) == -1)
    {
        perror("aio_read");
    }
}

void socket_aio_thread()
{
    auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(7834);
    auto paddr = (sockaddr *)&addr;
    bind(sock, paddr, sizeof(sockaddr_in));

    listen(sock, 5);

    socklen_t addr_len;
    char peername[25];
    getsockname(sock, paddr, &addr_len);

    while (true)
    {
        auto nsock = accept(sock, paddr, &addr_len);
        if (nsock == -1)
        {
            if (errno == EINTR)
                continue;

            perror("accept");
            break;
        }

        sprintf(peername, "%s:%d", inet_ntoa(addr.sin_addr), addr.sin_port);
        fdmap[nsock] = peername;
        std::cout << peername << " connected" << std::endl;

        aiocb acb = {0};
        acb.aio_fildes = nsock;
        acb.aio_buf = malloc(BUFFER_SIZE + 1);
        acb.aio_nbytes = BUFFER_SIZE;
        acb.aio_offset = 0;
        acb.aio_sigevent.sigev_notify = SIGEV_THREAD;
        acb.aio_sigevent.sigev_notify_function = socket_aio_completion_handler;
        acb.aio_sigevent.sigev_notify_attributes = NULL;
        acb.aio_sigevent.sigev_value.sival_ptr = &acb;
        if (aio_read(&acb) == -1)
        {
            perror("aio_read");
        }
    }
}

void socket_aio()
{
    // socket_aio_signal();
    socket_aio_thread();
}

#include <thread>

void socket_udp_basic()
{
    std::thread([]()
                {
                    auto sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

                    char buf[100];

                    sockaddr_in addr = {0};
                    addr.sin_family = AF_INET;
                    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
                    addr.sin_port = 7834;
                    auto paddr = (sockaddr *)&addr;
                    bind(sock, paddr, sizeof(sockaddr_in));

                    socklen_t addr_len;

                    while (true)
                    {
                        auto len = recvfrom(sock, buf, 100, 0, paddr, &addr_len);
                        if (len > 0)
                        {
                            std::cout.write(buf, len);
                            std::cout << std::endl;
                        }
                    } })
        .detach();
    std::thread([]()
                {
                    auto sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

                    sockaddr_in addr = {0};
                    addr.sin_family = AF_INET;
                    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
                    addr.sin_port = 7834;
                    auto paddr = (sockaddr *)&addr;

                    while (true)
                    {
                        auto t = time(nullptr);
                        auto ts = ctime(&t);
                        sendto(sock, ts, strlen(ts), 0, paddr, sizeof(sockaddr_in));

                        sleep(1);
                    } })
        .detach();

    sleep(20);
}

// 原来问题在防火墙
void socket_udp_multicast()
{
    std::thread([]()
                {
                    auto sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

                    sockaddr_in addr = {0};
                    addr.sin_family = AF_INET;
                    addr.sin_addr.s_addr = inet_addr("239.0.0.54");
                    addr.sin_port = htons(8342);
                    auto paddr = (sockaddr *)&addr;

                    while (true)
                    {
                        auto t = time(nullptr);
                        auto ts = ctime(&t);
                        sendto(sock, ts, strlen(ts), 0, paddr, sizeof(sockaddr_in));

                        sleep(1);
                    } })
        .detach();
    std::thread([]()
                {
                    auto sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

                    sockaddr_in addr = {0};
                    addr.sin_family = AF_INET;
                    addr.sin_port = htons(8342);
                    auto paddr = (sockaddr *)&addr;
                    bind(sock, paddr, sizeof(sockaddr_in));

                    auto loop = 1;
                    setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));

                    ip_mreq mreq = {0};
                    mreq.imr_multiaddr.s_addr = inet_addr("239.0.0.54");
                    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
                    setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(ip_mreq));

                    char buf[100];
                    socklen_t addr_len;

                    while (true)
                    {
                        auto len = recvfrom(sock, buf, 100, 0, paddr, &addr_len);
                        if (len > 0)
                        {
                            std::cout.write(buf, len);
                            std::cout << std::endl;
                        }
                    } })
        .detach();

    sleep(20);
}

void socket_udp_broadcast()
{
    std::thread([]()
                {
                    auto sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

                    auto broad = 1;
                    setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(int));

                    sockaddr_in addr = {0};
                    addr.sin_family = AF_INET;
                    addr.sin_port = htons(8343);
                    addr.sin_addr.s_addr = inet_addr("192.168.0.255");
                    auto paddr = (sockaddr *)&addr;

                    while (true)
                    {
                        auto t = time(nullptr);
                        auto ts = ctime(&t);
                        sendto(sock, ts, strlen(ts), 0, paddr, sizeof(sockaddr_in));

                        sleep(1);
                    } })
        .detach();
    std::thread([]()
                {
                    auto sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

                    sockaddr_in addr = {0};
                    addr.sin_family = AF_INET;
                    addr.sin_port = htons(8343);
                    auto paddr = (sockaddr *)&addr;
                    bind(sock, paddr, sizeof(sockaddr_in));

                    char buf[50];
                    socklen_t addr_len;

                    while (true)
                    {
                        auto len = recvfrom(sock, buf, 50, 0, paddr, &addr_len);
                        if (len > 0)
                        {
                            std::cout.write(buf, len);
                            std::cout << std::endl;
                        }
                    } })
        .detach();

    sleep(20);
}

void socket_udp()
{
    // socket_udp_basic();

    // socket_udp_multicast();

    socket_udp_broadcast();
}

// 链路层 需要特殊权限
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

void socket_packet()
{
    auto sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

    char buf[2048];
    auto eth = (ethhdr *)buf;

    in_addr addr;

    while (true)
    {
        auto len = recvfrom(sock, buf, 2048, 0, nullptr, nullptr);
        if (eth->h_proto == htons(ETH_P_IP))
        {
            auto ip = (iphdr *)(eth + ETH_HLEN);

            addr.s_addr = ip->saddr;
            std::string sip = inet_ntoa(addr);
            addr.s_addr = ip->daddr;
            std::string dip = inet_ntoa(addr);

            if (ip->protocol == IPPROTO_TCP)
            {
                auto tcp = (tcphdr *)(ip + ip->ihl * 4);

                std::printf("%d: %s:%d-%s:%d\n", ip->protocol, sip.c_str(), tcp->th_sport, dip.c_str(), tcp->th_dport);
            }
            else
            {
                std::printf("%d: %s-%s\n", ip->protocol, sip.c_str(), dip.c_str());
            }
        }
        else
        {
            std::cout << eth->h_proto << "(" << len << "): " << eth->h_source << "-" << eth->h_dest << std::endl;
        }
    }
}

// 网络层 需要特殊权限
#include <netinet/ip_icmp.h>

unsigned short cal_chksum(unsigned short *addr, int len)
{
    int nleft = len;
    int sum = 0;
    unsigned short *w = addr;
    unsigned short answer = 0;

    /*把ICMP报头二进制数据以2字节为单位累加起来*/
    while (nleft > 1)
    {
        sum += *w++;
        nleft -= 2;
    }

    /*若ICMP报头为奇数个字节，会剩下最后一字节。把最后一个字节视为一个2字节数据的高字节，
      这个2字节数据的低字节为0，继续累加*/
    if (nleft == 1)
    {
        *(unsigned char *)(&answer) = *(unsigned char *)w;
        sum += answer;
    }

    /*校验和是以16位为单位进行求和计算的，sum是32位的，sum&0xffff是低16位，sum>>16是右移16位，
      取到的是高16位，相加就是高16位和低16位的和。*/
    sum = (sum >> 16) + (sum & 0xffff);

    /*这一步是有可能上面加的时候有进位到高16位的，再把高16位加进来。*/
    sum += (sum >> 16);

    /*上一步肯定不会再有进位了，即使上面sum高16位非0也不要紧，此处sum只能把低字节数赋值给answer，因为answer为16位*/
    answer = ~sum;

    return answer;
}

void socket_raw()
{
    auto sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("192.168.0.1");
    auto paddr = (sockaddr *)&addr;

    // 发送 ping 包
    char sendbuf[512];
    auto datalen = 56;
    auto icmp_msg = (icmphdr *)sendbuf;
    // icmp 头+内容
    auto len = 8 + datalen;
    icmp_msg->type = ICMP_ECHO;
    icmp_msg->code = 0;
    icmp_msg->un.echo.id = 1;
    icmp_msg->un.echo.sequence = 1;
    icmp_msg->checksum = 0;
    icmp_msg->checksum = cal_chksum((unsigned short *)icmp_msg, len);
    auto retval = sendto(sock, sendbuf, len, 0, paddr, sizeof(addr));
    std::cout << "send " << retval << std::endl;

    // 接收
    char recvbuf[2048];

    while (true)
    {
        len = recvfrom(sock, recvbuf, 2048, 0, nullptr, nullptr);
        //接收到的数据为L3+L4+负载
        std::cout << "recv " << len << std::endl;
    }
}

#include <termios.h>

void serial()
{
    auto fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd == -1)
    {
        perror("open");
        return;
    }

    termios opt = {0};
    cfsetspeed(&opt, B19200);

    // 8-N-1
    opt.c_cflag &= ~CSIZE;
    opt.c_cflag |= CS8;
    opt.c_cflag &= ~PARENB;
    opt.c_cflag &= ~CSTOPB;

    tcflush(fd, TCIOFLUSH);

    tcsetattr(fd, TCSANOW, &opt);

    char buf[64];

    while (true)
    {
        auto len = read(fd, buf, 64);
        if (len == -1)
        {
            perror("read");

            usleep(200000);
            continue;
        }

        std::cout.write(buf, len);
        std::cout << std::endl;
    }

    close(fd);
}

#include <sys/time.h>

void signals()
{
    struct sigaction action = {0};
    action.sa_handler = [](int sig)
    { std::cout << strsignal(sig) << std::endl; };
    sigaddset(&action.sa_mask, SIGQUIT);

    if (sigaction(SIGUSR1, &action, NULL) == -1)
    {
        perror("signal");
        return;
    }

    if (sigaction(SIGUSR2, &action, NULL) == -1)
    {
        perror("signal");
        return;
    }

    if (sigaction(SIGALRM, &action, NULL) == -1)
    {
        perror("signal");
        return;
    }

    alarm(5);

    itimerval val = {0};
    val.it_interval.tv_sec = 10;
    val.it_value.tv_sec = 2;
    setitimer(ITIMER_REAL, &val, nullptr);

    while (true)
    {
        raise(SIGUSR1);

        sleep(1);
    }
}

#include <sys/stat.h>
#include <sys/xattr.h>
#include <sys/inotify.h>

void file_dir()
{
    struct stat fst = {0};
    if (stat("./sqlite.db", &fst) == -1)
    {
        perror("stat");
    }
    else
    {
        std::cout << fst.st_uid << " " << fst.st_size << std::endl;
    }

    char buf[512];
    auto ret = listxattr("./a.out", buf, 512);
    if (ret == -1)
        perror("listxattr");
    else if (ret > 0)
    {
        std::cout.write(buf, ret);
        std::cout << std::endl;
    }

    mkdir("tst", S_IRWXU | S_IWUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
    rmdir("rst");

    std::cout << getcwd(nullptr, 0) << std::endl;
    std::cout << get_current_dir_name() << std::endl;

    link("sqlite.db", "sqlite2.db");
    unlink("sqlite2.db");

    auto fd = inotify_init();
    inotify_add_watch(fd, "./", IN_ACCESS);

    char buf2[sizeof(inotify_event) * 10];
    auto ie = (inotify_event *)buf;

    while (true)
    {
        auto len = read(fd, buf2, sizeof(inotify_event) * 10);
        for (inotify_event *ev = ie; ev < ie + len; ev++)
        {
            std::cout << ev->name << std::endl;
        }
    }

    close(fd);
}

void process()
{
}

#define PIPE_NAME "ljdsfa"

void ipc_pipe()
{
    if (mkfifo(PIPE_NAME, S_IRUSR | S_IWUSR) == -1)
    {
        perror("mkfifo");
        // return;
    }

    auto fd = open(PIPE_NAME, O_WRONLY);
    if (fd == -1)
    {
        perror("open");
        return;
    }

    std::thread([]()
                {
                    auto fd = open(PIPE_NAME, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
                    if (fd == -1)
                    {
                        perror("open");
                        return;
                    }

                    char buf[50];

                    while (true)
                    {
                        auto len = read(fd, buf, 50);
                        if (len == -1)
                        {
                            perror("read");
                        }
                        else
                        {
                            std::cout.write(buf, len);
                            std::cout << std::endl;
                        }
                    } })
        .detach();

    while (true)
    {
        auto t = time(nullptr);
        auto ts = ctime(&t);
        if (write(fd, ts, strlen(ts)) == -1)
        {
            perror("write");
        }

        sleep(1);
    }
}

#include <sys/ipc.h>
#include <sys/msg.h>

void ipc_queue()
{
    auto key = ftok("sqlite.db", 35);
    if (key == -1)
    {
        perror("ftok");
        return;
    }

    auto fd = msgget(key, IPC_CREAT);
    if (fd == -1)
    {
        perror("msgget");
        return;
    }

    std::thread([key]()
                {
                    auto fd = msgget(key, 0);
                    if (fd == -1)
                    {
                        perror("msgget");
                        return;
                    }

                    char buf[50];

                    while (true)
                    {
                        auto len = msgrcv(fd, buf, 50, 0, 0);
                        if (len == -1)
                        {
                            perror("msgrcv");

                            sleep(1);
                            continue;
                        }

                        std::cout.write(buf, len);
                        std::cout << std::endl;
                    } })
        .detach();

    while (true)
    {
        auto t = time(nullptr);
        auto ts = ctime(&t);
        if (msgsnd(fd, ts, strlen(ts), 0) == -1)
        {
            perror("msgsnd");
        }

        sleep(1);
    }
}

void ipc_semphore()
{
}

void ipc()
{
    // ipc_pipe();
    // ipc_queue();
    // ipc_semphore();
}

#include <sys/mman.h>
#include <malloc.h>

void mem()
{
    auto buf = malloc(1024);
    mlock(buf, 1024);

    std::cout << getpagesize() << std::endl;

    malloc_stats();
}

void conc()
{
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

    pthread_t thid;
    pthread_create(
        &thid, nullptr, [](void *args) -> void *
        {
            auto cond = (pthread_cond_t *)args;
            pthread_detach(pthread_self());

            for (int i = 0; i < 20; i++)
            {
                auto t = time(nullptr);
                auto ts = ctime(&t);
                std::cout << ts << std::endl;

                sleep(1);
            }

            pthread_cond_signal(cond);
            return nullptr; },
        &cond);

    pthread_mutex_t locker = PTHREAD_MUTEX_INITIALIZER;

    pthread_mutex_lock(&locker);
    pthread_cond_wait(&cond, &locker);
    pthread_mutex_unlock(&locker);

    pthread_cond_destroy(&cond);
}

#include <stdexcept>

class Exception : public std::runtime_error
{
public:
    Exception(const std::string &what);
    ~Exception() noexcept override = default;

    std::string stack_trace() const noexcept
    {
        return m_stack;
    }

private:
    void fill_stack_trace();

private:
    std::string m_stack;
};

#include <cxxabi.h>
#include <execinfo.h>

Exception::Exception(const std::string &what) : std::runtime_error(what)
{
    fill_stack_trace();
}

void Exception::fill_stack_trace()
{
    const int maxFrames = 200; // 返回堆栈框架的最大个数
    void *frame[maxFrames];    // 存放堆栈框架的的返回地址
    int nptrs = ::backtrace(frame, maxFrames);
    char **strings = ::backtrace_symbols(frame, nptrs); // 字符串数组
    if (strings == nullptr)
    {
        return;
    }

    size_t len = 256;
    char *demangled = static_cast<char *>(::malloc(len)); // 用于存放demangled之后的结果
    for (int i = 1; i < nptrs; ++i)
    {
        char *leftPar = nullptr; // 左括号
        char *plus = nullptr;    // 加号
        for (char *p = strings[i]; *p; ++p)
        { // 找到左括号和加号的位置，两者之间的内容是需要demangle的
            if (*p == '(')
                leftPar = p;
            else if (*p == '+')
                plus = p;
        }

        *plus = '\0';
        int status = 0;
        char *ret = abi::__cxa_demangle(leftPar + 1, demangled, &len, &status);
        *plus = '+';
        if (status != 0)
        {
            m_stack.append(strings[i]);
        }
        else
        {
            demangled = ret;
            m_stack.append(strings[i], leftPar + 1);
            m_stack.append(demangled);
            m_stack.append(plus);
        }

        m_stack.push_back('\n');
    }

    free(demangled);
    free(strings);
}

#include <iostream>

class ExceptionTest
{
public:
    ExceptionTest()
    {
        throw Exception("hello world");
    }
};

void test_func()
{
    ExceptionTest b;
}

void test_callstack()
{
    try
    {
        test_func();
    }
    catch (Exception &e)
    {
        std::cout << e.what() << '\n';
        std::cout << e.stack_trace() << std::endl;
    }
}

int main()
{
    // socket_basic();
    // socket_nonblock();
    // socket_select();
    // socket_poll();
    // socket_epoll();
    // socket_signal();
    // socket_aio();
    // socket_udp();
    // socket_packet();
    // socket_raw();
    // serial();
    // signals();

    // file_dir();
    // process();
    // ipc();
    // mem();
    // conc();

    test_callstack();

    return 0;
}
